/**
 * Copyright (c) 2017-2024 Nop Platform. All rights reserved.
 * Author: canonical_entropy@163.com
 * Blog:   https://www.zhihu.com/people/canonical-entropy
 * Gitee:  https://gitee.com/canonical-entropy/nop-entropy
 * Github: https://github.com/entropy-cloud/nop-entropy
 */
package com.gitee.wsl.text.marker

import com.gitee.wsl.func.Supplier
import com.gitee.wsl.func.Function

/**
 * 类似于String和StringBuilder的关系。MarkedString为不可变对象，而MarkedStringBuilderT为可变对象
 *
 * @param <T>
</T> */
abstract class MarkedStringBuilderT<T : MarkedStringBuilderT<T>> : IMarkedString {
    // 这里有一个小优化，当只有一个append语句时不会生成StringBuilder对象。
    private var buf: StringBuilder? = null

    // 第一次append调用会缓存在这里
    private var firstText: String? = null

    final override var markers: MutableList<Marker> = mutableListOf()

    constructor()

    constructor(buf:StringBuilder?) {
        this.buf = buf
    }

    constructor(text: String?, markers: MutableList<Marker>) {
        this.firstText = text
        this.markers = markers
    }

    constructor(str: IMarkedString) {
        this.firstText = str.text
        this.markers = ArrayList<Marker>(str.markers)
    }

    fun clear() {
        this.firstText = null
         buf?.setLength(0)
         markers.clear()
    }

    abstract fun castReturn(): T

    fun append(text: CharSequence?): T {
        if (text == null) return castReturn()

        if (buf == null && firstText == null) {
            firstText = text.toString()
        } else {
            makeBuf().append(text)
        }
        return castReturn()
    }

    fun appendChars(chars: CharArray?, offset: Int, len: Int): T {
        makeBuf().append(chars, offset, len)
        return castReturn()
    }

    fun append(chars: CharSequence?, start: Int, end: Int): T {
        makeBuf().append(chars, start, end)
        return castReturn()
    }

    fun makeBuf(): StringBuilder {
        if (buf == null) {
            buf = StringBuilder()
            if (firstText != null) {
                buf!!.append(firstText)
                firstText = null
            }
        }
        return buf!!
    }

    override val isEmpty: Boolean
        get() = length() == 0

    /*override fun getMarkers(): List<Marker> {
        return if (markers == null) emptyList() else markers
    }*/

    fun clearMarkers(): T {
        this.markers.clear()
        return castReturn()
    }

    fun getMarker(index: Int): Marker? {
        if (markers.isEmpty()) return null
        return markers[index]
    }

    fun charAt(index: Int): Char {
        if (buf == null) {
            require(!firstText.isNullOrEmpty()) { "nop.err.commons.text.null-marked-string" }
            return firstText!![index]
        }
        return buf!![index]
    }

    override val textSequence: CharSequence
        get() {
            return buf?:firstText?:""
        }

    override fun toString(): String {
        return text
    }

    fun append(c: Char): T {
        makeBuf().append(c)
        return castReturn()
    }

    /**
     * 从尾部删除n个字符，如果marker与被删除的位置重叠，则也会被自动删除
     *
     * @param len
     * @return
     */
    fun deleteTail(len: Int): T {
        val buf = makeBuf()
        buf.deleteRange(buf.length - len, buf.length)
        
            val length: Int = buf.length
            for (i in markers.indices.reversed()) {
                val marker = markers[i]
                if (marker.textEnd <= length) break
                markers.removeAt(i)
            }
        
        return castReturn()
    }

    val lastMarker: Marker?
        get() {
            if ( markers.isEmpty()) return null
            return markers[markers.size - 1]
        }

    fun checkAddMarker(marker: Marker) {
        val lastMarker = this.lastMarker
        if (lastMarker != null) {
            if (lastMarker.intersect(marker) || lastMarker.textBegin >= marker.textBegin) 
                throw IllegalArgumentException("ERR_TEXT_MARKER_POS_CONFLICT")
                   /* .param(ARG_PREV_MARKER, lastMarker)
                .param(ARG_MARKER, marker)*/
        }

        if (marker.textEnd > length()) 
            throw IllegalArgumentException("ERR_TEXT_INVALID_MARKER_RANGE")
           /* .param(
            ARG_MARKER,
            marker
        ).param(ARG_LENGTH, length())*/
    }

    fun appendMarker(marker: Marker): T {
        checkAddMarker(marker)
        addMarker(marker)
        return castReturn()
    }

    fun appendMarkers(markers: List<Marker>): T {
        for (marker in markers) {
            addMarker(marker)
        }
        return castReturn()
    }

    fun insertMarker(marker: Marker): T {
        if (markers == null) {
            addMarker(marker)
            return castReturn()
        }

        val idx: Int = this.markers.indexOf(marker)
        if (idx >= 0) 
            throw IllegalArgumentException("ERR_TEXT_MARKER_POS_CONFLICT")
               /* .param(ARG_MARKER, marker)
            .param(
                ARG_PREV_MARKER,
                markers!![idx]
            )*/

        if (marker.textEnd > length()) 
            throw IllegalArgumentException("ERR_TEXT_INVALID_MARKER_RANGE")
            /*.param(
            ARG_MARKER,
            marker
        ).param(ARG_LENGTH, length())*/

        val pos = -idx - 1
        if (pos > 0) {
            val prev = markers[pos - 1]
            if (prev.intersect(marker)) 
                throw IllegalArgumentException("ERR_TEXT_MARKER_POS_CONFLICT")
                   /* .param(
                ARG_MARKER,
                marker
            ).param(
                ARG_PREV_MARKER,
                prev
            )*/
        }

        if (pos < markers.size) {
            val next = markers[pos]
            if (next.intersect(marker)) 
                throw IllegalArgumentException("ERR_TEXT_MARKER_POS_CONFLICT")
                    /*.param(
                ARG_MARKER,
                marker
            ).param(
                ARG_PREV_MARKER,
                next
            )*/
        }
        markers.add(pos, marker)

        return castReturn()
    }

    protected fun addMarker(marker: Marker) {
        //if (markers == null) markers = java.util.ArrayList<Marker>()
        markers.add(marker)
    }

    fun addMarkers(markers: Collection<Marker>): T {
            for (marker in markers) {
                addMarker(marker)
            }
        return castReturn()
    }

    /**
     * 追加一段MarkedString，合并文本，并将markers逐个偏移offset之后加入到当前marker集合
     *
     * @param str
     * @return
     */
    fun append(str: IMarkedString): T {
        val offset = length()
        append(str.textSequence)
        val markers = str.markers
        if (markers != null) {
            var i = 0
            val n = markers.size
            while (i < n) {
                var marker= markers[i]
                marker = marker.offset(offset)!!
                // 假设marker已经排好序，无需再检查是否与其他marker重叠
                this.addMarker(marker)
                i++
            }
        }
        return castReturn()
    }

    fun append(o: Any?): T {
        if (o == null) return castReturn()

        return when (o) {
            is IMarkedString -> {
                append(o)
            }
            is CharSequence -> {
                append(o as CharSequence?)
            }
            else -> {
                append(o.toString())
            }
        }
    }

    fun appendWithMarker(text: String, marker: Marker): T {
        this.append(text).addMarker(marker)
        return castReturn()
    }

    fun appendWithValueMarker(text: String?, name: String?, value: Any?, masked: Boolean): T {
        val buf = this.makeBuf()
        val start: Int = buf.length
        buf.append(text)
        val end: Int = buf.length
        return appendMarker(ValueMarker(start, end, name, value, masked))
    }

    fun markWithName(text: String?, name: String?): T {
        val buf = this.makeBuf()
        val start: Int = buf.length
        buf.append(text)
        val end: Int = buf.length
        return appendMarker(NameMarker(start, end, name))
    }

    fun markWithProvider(
        text: String?,
        name: String?,
        provider: Supplier<*>?,
        masked: Boolean
    ): T {
        val buf = this.makeBuf()
        val start: Int = buf.length
        buf.append(text)
        val end: Int = buf.length
        return appendMarker(ProviderMarker(start, end, name, provider, masked))
    }

    fun markValue(text: String?, value: Any?, masked: Boolean): T {
        return appendWithValueMarker(text, null, value, masked)
    }

    override fun length(): Int {
        if (buf == null) {
            if (firstText == null) return 0
            return firstText!!.length
        }
        return buf!!.length
    }

    /**
     * 将ValueMarker的值替换为新的值
     *
     * @param values 列表的长度必须与ValueMarker的个数一致
     * @return 当前的MarkedStringBuilder对象，用于链式调用
     */
    fun changeMarkerValues(values: List<Any?>): T {
        val markers = this.markers
        var k = 0
        var i = 0
        val n = markers.size
        while (i < n) {
            var marker = markers[i]
            if (marker is ValueMarker) {
                if (k >= values.size) {
                    k++
                    i++
                    continue
                }

                val value = values[k]
                k++
                marker = marker.changeValue(value)
                markers[i] = marker
            }
            i++
        }
        if (k != values.size)
            throw IllegalArgumentException("ERR_TEXT_MAKER_COUNT_MISMATCH")
                /*.param(
            ARG_TEXT,
            text
        )
            .param(ARG_COUNT, values.size).param(ARG_EXPECTED_COUNT, valueMarkerCount)*/
        return castReturn()
    }

    fun changeMarker(transformer: Function<Marker?, Marker?>): T {
        val markers = this.markers
        var i = 0
        var n = markers.size
        while (i < n) {
            var marker:Marker? = markers[i]
            marker = transformer.apply(marker)
            if (marker == null) {
                markers.removeAt(i)
                i--
                n--
            } else {
                markers[i] = marker
            }
            i++
        }
        return castReturn()
    }

    fun appendRange(str: IMarkedString, startPos: Int, endPos: Int): T {
        val seq: CharSequence = str.subSequence(startPos, endPos)
        val pos = length()
        this.append(seq)

        for (marker in str.markers) {
            if (marker.within(startPos, endPos)) {
                marker.offset(-startPos + pos)?.let { addMarker(it) }
            }
        }
        return castReturn()
    }

    fun insertAt(startPos: Int, str: IMarkedString): T {
        val sb = makeBuf()
        sb.insert(startPos, str.textSequence)
        if (str.markers.isNotEmpty() || this.markers != null) {
            val markers = mutableListOf<Marker>()
            var index = -1
            if (this.markers != null) {
                index = 0
                while (index < this.markers.size) {
                    val marker = this.markers[index]
                    if (marker.textBegin >= startPos) break
                    index++
                }
            }

            str.markers.forEach{ marker: Marker ->
                markers.add(marker.offset(startPos))
            }

            if (index >= 0) {
                while (index < this.markers.size) {
                    markers.add(this.markers[index].offset(str.length()))
                    index++
                }
            }
            this.markers = markers
        }
        return castReturn()
    }

    fun removeRange(startPos: Int, endPos: Int): T {
        val sb  = makeBuf()
        sb.deleteRange(startPos, endPos)
        if ( markers.isNotEmpty()) {
            val markers = ArrayList<Marker>(
                markers.size
            )

            for (index in this.markers.indices) {
                var marker:Marker? = this.markers[index]
                if (marker!!.within(startPos, endPos)) continue
                if (marker.textBegin > endPos) {
                    marker = marker.offset(startPos - endPos)
                }
                markers.add(marker)
            }

            this.markers = markers
        }
        return castReturn()
    }

    /**
     * 将marker转换为MarkedString对象，后续的marker位置将被自动调整。
     *
     * @param transformer 入口参数的marker总是保持原始位置。
     * @return 当前对象
     */
    fun transformMarker(transformer: Function<Marker, IMarkedString?>): T {
        val markers = markers
        var offset = 0
        var i = 0
        var n = markers.size
        while (i < n) {
            var marker = markers[i]
            val str = transformer.apply(marker)
            if (str != null) {
                markers.removeAt(i)
                i--
                n--
                val text: String = str.text
                val begin = marker.textBegin + offset
                val buf = makeBuf()
                buf.replaceRange(begin, marker.textEnd + offset, text)
                offset += text.length - marker.length()
                for (strMarker in str.markers) {
                    strMarker.offset(begin)?.let { markers.add(i + 1, it) }
                    i++
                    n++
                }
            } else {
                markers[i] = marker.offset(offset)!!
            }
            i++
        }
        return castReturn()
    }

    /**
     * 追加一段MarkedString，并把其中的Marker替换为transformer返回的对象
     *
     * @param source      追加的MarkedString
     * @param transformer 如果返回null, 则表示保持原先的marker
     */
    fun appendWithTransform(
        source: IMarkedString,
        transformer: Function<Marker, IMarkedString?>
    ): T {
        // String text = source.getText();

        val markers: List<Marker> = source.markers
        if (markers.isEmpty()) {
            append(source.textSequence)
            return castReturn()
        }

        val text: CharSequence = source.textSequence
        var prevTransformedMarkerIndex = -1

        var offset = 0
        var i = 0
        val n = markers.size
        while (i < n) {
            val marker = markers[i]
            val str = transformer.apply(marker)
            if (str == null) {
                // 保持原有marker
                i++
                continue
            }

            val lastPos =
                if (prevTransformedMarkerIndex < 0) 0 else markers[prevTransformedMarkerIndex].textEnd
            append(text, lastPos, marker.textBegin)
            for (k in prevTransformedMarkerIndex + 1 until i) {
                var prevMarker: Marker? = markers[k]
                if (offset != 0) prevMarker = prevMarker!!.offset(offset)
                addMarker(prevMarker!!)
            }
            offset += str.length() - marker.length()

            append(str)
            prevTransformedMarkerIndex = i
            i++
        }

        val lastPos =
            if (prevTransformedMarkerIndex < 0) 0 else markers[prevTransformedMarkerIndex].textEnd
        append(text, lastPos, text.length)
        for (k in prevTransformedMarkerIndex + 1 until markers.size) {
            var prevMarker: Marker? = markers[k]
            if (offset != 0)
                prevMarker = prevMarker!!.offset(offset)
            addMarker(prevMarker!!)
        }
        return castReturn()
    }
}